// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.EntityFrameworkCore.Design.Internal;
using Microsoft.EntityFrameworkCore.Sqlite.Design.Internal;
using Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal;
using NetTopologySuite;

namespace Microsoft.EntityFrameworkCore.Migrations.Design;

public class CSharpMigrationsGeneratorSqliteTest : CSharpMigrationsGeneratorTestBase
{
    protected virtual string AddBoilerPlate(string code, bool usingSystem = false, bool usingMetadata = true)
        => $$"""
// <auto-generated />
{{(usingSystem
    ? @"using System;
"
    : "")}}using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
{{(usingMetadata
    ? @"using Microsoft.EntityFrameworkCore.Metadata;
"
    : "")}}using Microsoft.EntityFrameworkCore.Storage.ValueConversion;

#nullable disable

namespace RootNamespace
{
    [DbContext(typeof(DbContext))]
    partial class Snapshot : ModelSnapshot
    {
        protected override void BuildModel(ModelBuilder modelBuilder)
        {
#pragma warning disable 612, 618
            modelBuilder.HasDefaultSchema("DefaultSchema");

{{code}}
#pragma warning restore 612, 618
        }
    }
}

""";

    [ConditionalFact]
    public void Autoincrement_annotation_is_replaced_by_extension_method_call_in_snapshot()
    {
        Test(
            builder =>
            {
                builder.Entity<EntityWithAutoincrement>(e =>
                {
                    e.Property(p => p.Id).UseAutoincrement();
                });
            },
            AddBoilerPlate("""
                        modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGeneratorTestBase+EntityWithAutoincrement", b =>
                            {
                                b.Property<int>("Id")
                                    .ValueGeneratedOnAdd()
                                    .HasColumnType("INTEGER");

                                SqlitePropertyBuilderExtensions.UseAutoincrement(b.Property<int>("Id"));

                                b.HasKey("Id");

                                b.ToTable("EntityWithAutoincrement", "DefaultSchema");
                            });
            """),
            model =>
            {
                var entity = model.FindEntityType(typeof(EntityWithAutoincrement));
                var property = entity!.FindProperty("Id");
                Assert.Equal(SqliteValueGenerationStrategy.Autoincrement, Microsoft.EntityFrameworkCore.SqlitePropertyExtensions.GetValueGenerationStrategy(property!));
            });
    }

    [ConditionalFact]
    public void Autoincrement_works_with_value_converter_to_int()
    {
        Test(
            builder =>
            {
                builder.Entity<EntityWithConverterPk>(e =>
                {
                    e.Property(p => p.Id).HasConversion<int>().UseAutoincrement();
                });
            },
            AddBoilerPlate("""
                        modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGeneratorTestBase+EntityWithConverterPk", b =>
                            {
                                b.Property<int>("Id")
                                    .ValueGeneratedOnAdd()
                                    .HasColumnType("INTEGER");

                                SqlitePropertyBuilderExtensions.UseAutoincrement(b.Property<int>("Id"));

                                b.HasKey("Id");

                                b.ToTable("EntityWithConverterPk", "DefaultSchema");
                            });
            """),
            model =>
            {
                var entity = model.FindEntityType(typeof(EntityWithConverterPk));
                var property = entity!.FindProperty("Id");
                Assert.Equal(SqliteValueGenerationStrategy.Autoincrement, Microsoft.EntityFrameworkCore.SqlitePropertyExtensions.GetValueGenerationStrategy(property!));
            });
    }

    [ConditionalFact]
    public void No_autoincrement_annotation_generated_for_non_autoincrement_property()
    {
        Test(
            builder =>
            {
                builder.Entity<EntityWithAutoincrement>(e =>
                {
                    e.Property(p => p.Id).ValueGeneratedNever();
                });
            },
            AddBoilerPlate("""
                        modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGeneratorTestBase+EntityWithAutoincrement", b =>
                            {
                                b.Property<int>("Id")
                                    .HasColumnType("INTEGER");

                                b.HasKey("Id");

                                b.ToTable("EntityWithAutoincrement", "DefaultSchema");
                            });
            """, usingMetadata: false),
            model =>
            {
                var entity = model.FindEntityType(typeof(EntityWithAutoincrement));
                var property = entity!.FindProperty("Id");
                Assert.Equal(SqliteValueGenerationStrategy.None, Microsoft.EntityFrameworkCore.SqlitePropertyExtensions.GetValueGenerationStrategy(property!));
            });
    }

    protected override TestHelpers TestHelpers
        => SqliteTestHelpers.Instance;

    protected override SqliteTestHelpers.TestModelBuilder CreateConventionalModelBuilder()
        => TestHelpers.CreateConventionBuilder(
            addServices: SqliteNetTopologySuiteServiceCollectionExtensions.AddEntityFrameworkSqliteNetTopologySuite);

    protected override CSharpMigrationsGenerator CreateMigrationsGenerator()
    {
        var sqliteTypeMappingSource = new SqliteTypeMappingSource(
            TestServiceFactory.Instance.Create<TypeMappingSourceDependencies>(),
            new RelationalTypeMappingSourceDependencies(
                [new SqliteNetTopologySuiteTypeMappingSourcePlugin(NtsGeometryServices.Instance)]));

        var codeHelper = new CSharpHelper(sqliteTypeMappingSource);

        var sqliteAnnotationCodeGenerator = new SqliteAnnotationCodeGenerator(
            new AnnotationCodeGeneratorDependencies(sqliteTypeMappingSource));

        var generator = new CSharpMigrationsGenerator(
            new MigrationsCodeGeneratorDependencies(
                sqliteTypeMappingSource,
                sqliteAnnotationCodeGenerator),
            new CSharpMigrationsGeneratorDependencies(
                codeHelper,
                new CSharpMigrationOperationGenerator(
                    new CSharpMigrationOperationGeneratorDependencies(
                        codeHelper)),
                new CSharpSnapshotGenerator(
                    new CSharpSnapshotGeneratorDependencies(
                        codeHelper, sqliteTypeMappingSource, sqliteAnnotationCodeGenerator))));

        return generator;
    }

    protected override ICollection<BuildReference> GetReferences()
        => new List<BuildReference>
        {
            BuildReference.ByName("Microsoft.EntityFrameworkCore"),
            BuildReference.ByName("Microsoft.EntityFrameworkCore.Abstractions"),
            BuildReference.ByName("Microsoft.EntityFrameworkCore.Relational"),
            BuildReference.ByName("Microsoft.EntityFrameworkCore.Sqlite"),
            BuildReference.ByName("Microsoft.EntityFrameworkCore.Design.Tests")
        };

    protected override IServiceCollection GetServices()
        => new ServiceCollection().AddEntityFrameworkSqliteNetTopologySuite();
}
